<template>
    <div class="canvas">
        <h3>消息统计</h3>
        <div class="search">
            <label for="type">分类：</label>
            <el-select id="type" v-model="seleType" style="width:1.5rem" @change="selectChange">
                <el-option value="day" label="天"></el-option>
                <el-option value="month" label="月"></el-option>
                <el-option value="year" label="年"></el-option>
            </el-select>
            <span></span>
            <label for="time">按时间查询：</label>
            <el-date-picker id="time" style="width: 3.5rem;" v-model="selectTime" :default-time="defTime" @change="selectChangeTime"
                type="daterange" range-separator="到" start-placeholder="开始日期" end-placeholder="结束日期" />
            <br>
            <label for="type">图形：</label>
            <el-checkbox v-model="treeChartType" label="树状图" @change="checkoutChangeType" />
            <el-checkbox v-model="lineChartType" label="折线图" @change="checkoutChangeType" />
            <br>
        </div>
        <div class="box">
            <canvas id="canvas" width="810" height="305" @mousemove="canvasMousemove">
                你的浏览器不支持canvas，快去升级浏览器吧！
            </canvas>

            <transition name="el-fade-in">
                <div class="tk" id="tk" v-show="isShowTk">
                    <div>日期：{{ tkValue.xVal }}</div>
                    <div>数量：{{ tkValue.yVal }} 条</div>
                   
                </div>

            </transition>

            <transition name="el-fade-in" v-show="isShowTk">
                <div id="dot" class="dot"></div>
            </transition>
           
        </div>

    </div>

</template>

<script setup>
import { ref, reactive, onMounted, nextTick } from 'vue'
/**
 * @Author dingxing [2023-03-09 14:28:29]
 * @Description 
 */
import $http from '@/api'
let canvas = null;
let ctx = null;
let Xdistance = 65;
let Ydistance = 50; //间距 px
let ySpan = 0;//间距 真实值
const chartData = ref({});
const showData = ref({}); //最多为7个
const coords = ref([]);
const data = ref({});
const seleType = ref('day');
const selectChart = ref('lineChart');
const treeChartType = ref(true);
const lineChartType = ref(true);
// 默认时刻
const defTime = ref([new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]);
const selectTime = ref('')

const isShowTk = ref(false);
const tkValue = ref({});
onMounted(() => {
    getMessageList();
});

const getMessageList = () => {
    $http.get('/messageList').then((res) => {
        // 备份一份源数据
        data.value = JSON.parse(JSON.stringify(res));
        const reslut = res;
        getData(reslut, 'day');
        // if (Object.keys(chartData.value).length > 10 && Object.keys(chartData.value).length < 15) {
        //     Xdistance = 45;
        // }else {
        //     Xdistance = 40;
        // }
        initCanvas();
        // 第一条
        // drawLine({
        //     coordinates: [{ x: 50, y: 10 }, { x: 75, y: 60 }, { x: 100, y: 10 }, { x: 125, y: 60 }, { x: 150, y: 10 }],lineColor: 'red',
        //     lineWidth: 6,lineJoin: 'bevel',lineCap: 'round',
        // });

        drawLineChart();
    })
};

// 事件
const selectChange = () => {
    selectTime.value = [];
    getData(data.value, seleType.value);
    drawLineChart();
};

//事件
const selectChangeTime = () => {
    if (!selectTime) return;
    seleType.value = 'day';
    const timeList = selectTime.value.map((date) => {
        return date.format('yyyy-MM-dd hh:mm:ss');
    })
    let result = data.value.filter((item) => {
        if (item.time >= timeList[0] && item.time <= timeList[1]) {
            return true
        }
    })

    getData(result, seleType.value);
    drawLineChart();

};

const canvasMousemove = (e) => {
    let offsetX = e.offsetX;
    let offsetY = e.offsetY;
    coords.value.some((cood, index) => {
        // 20像素
        if (cood.x <= offsetX + 20 && cood.x >= offsetX - 20 && cood.y <= offsetY + 20 && cood.y >= offsetY - 20) {
            isShowTk.value = true;
            const key = Object.keys(showData.value)[index];
            tkValue.value = {
                xVal: key,
                yVal: showData.value[key]
            }
            const tk = document.getElementById('tk');
            const dot = document.getElementById('dot');
            tk.style.top = cood.y + 'px';
            tk.style.left = cood.x + 'px';
            dot.style.top = cood.y + 'px';
            dot.style.left = cood.x + 'px';

            return true
        }else {
            isShowTk.value = false;
        }
    })
};
const checkoutChangeType = () => {
    drawLineChart();
};
/**
 * 
 * @param {Array} reslut 
 * @param {String} type  默认day,month,year
 */
const getData = (reslut, type = 'day') => {
    reslut = JSON.parse(JSON.stringify(reslut));
    chartData.value = {};
    showData.value = {};
    // 以 日分隔
    reslut.forEach((item) => {
        if (type == 'day') {
            item.time = item.time.slice(0, 10)
        } else if (type == 'month') {
            item.time = item.time.slice(2, 7)
        } else if (type == 'year')
            item.time = item.time.slice(0, 4)
    });

    reslut.forEach((item) => {
        if (!chartData.value[item.time]) {
            chartData.value[item.time] = 1;
        } else {
            chartData.value[item.time]++;
        }
    })
    let keys = Object.keys(chartData.value);
    // // 大于7切割 默认最后面 7个
    if (keys.length > 7) {
        let getVal = keys.slice(keys.length - 7);
        getVal.forEach((key) => {
            showData.value[key] = chartData.value[key];
        })
    } else {
        keys.forEach((key) => {
            showData.value[key] = chartData.value[key];
        })
    }
};
const initCanvas = () => {
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');
};

// 绘图 ，动态更新数据调用此方法更新页面
const drawLineChart = () => {
    // 每次绘图前清除原来的画布
    ctx.clearRect(0, 0, 510, 305);
    // Y轴线
    drawLine({ coordinates: [{ x: 15, y: 10 }, { x: 15, y: 285 }], lineColor: 'skyblue' });
    // Y箭头
    drawLine({ coordinates: [{ x: 12, y: 15 }, { x: 15, y: 8 }, { x: 18, y: 15 }], lineColor: 'skyblue', lineJoin: "round", lineWidth: 1.5 });
    // X轴线
    drawLine({ coordinates: [{ x: 15, y: 285 }, { x: 510, y: 285 }], lineColor: 'skyblue' });
    // X箭头
    drawLine({ coordinates: [{ x: 505, y: 282 }, { x: 510, y: 285 }, { x: 505, y: 288 }], lineColor: 'skyblue', lineJoin: "round", lineWidth: 1.5 });
    drawFont({ text: '0', fontSize: '13px', x: 4, y: 290, fillColor: 'skyblue' });
    // Y 轴文字数据
    YData();
    //  X轴文字数据
    XData();
    function YData() {
        let yMax = maxVal(Object.values(showData.value)); // 只能放 5个数
        let int = String(yMax / 5);
        // 判断是否有小数，有就 + 1
        if (int.indexOf('.') == -1) {
            ySpan = parseInt(yMax / 5)
        } else {
            ySpan = parseInt(yMax / 5) + 1;
        }
        for (let i in [1, 2, 3, 4, 5]) {
            const text = '' + (ySpan + ySpan * i);
            const len = text.length;
            drawFont({ text: text, fontSize: '13px', x: len == 1 ? 5 : 2, y: 238 - Ydistance * i, fillColor: 'skyblue' }); drawLine({ coordinates: [{ x: 15, y: 235 - Ydistance * i }, { x: 19, y: 235 - Ydistance * i }], lineWidth: 1.5, lineColor: 'skyblue' })
        }
        drawFont({ text: 'Y', fontSize: '13px', x: 1, y: 10, fillColor: 'skyblue' });
    }
    function XData() {
        let i = 0;
        const len = Object.keys(showData.value).length;
        for (let key in showData.value) {
            drawFont({ text: key, fontSize: '13px', x: Xdistance - 15 + (Xdistance) * i, y: 297, fillColor: 'skyblue' }); drawLine({ coordinates: [{ x: Xdistance + 15 + (Xdistance) * i, y: 285, }, { x: Xdistance + 15 + Xdistance * i, y: 281, }], lineWidth: 1.5, lineColor: 'skyblue' })
            i++;
        }
        drawFont({ text: 'X', fontSize: '13px', x: 500, y: 299, fillColor: 'skyblue' });
    }

    function run() {
        let i = 0;
        let list = [];
        for (let key in showData.value) {
            list.push(transVal(i, showData.value[key]))
            i++;
        }
        return list;
    }
    coords.value = run();
    // 画树状图
    if (treeChartType.value) {
        (function () {
            let i = 0;
            for (let key in showData.value) {
                drawRect({
                    coordinates: [transVal(i, showData.value[key]).x - (Xdistance / 4), transVal(i, showData.value[key]).y, Xdistance / 2, 285 - transVal(i, showData.value[key]).y],
                    type: 'fill', fillColor: 'skyblue',
                    globalAlpha: 1
                })
                i++;
            }
        })()
    }

    if (lineChartType.value) {
        // 画折线
        drawLine({
            coordinates: coords.value,
            lineColor: 'red', lineJoin: "round", lineWidth: 1.5
        });
    }
};

// 数量转 
/**
 * 
 * @param {Number} n 间距像素除以真实值 间距
 * @param {*} yVal  y像素
 * @param {*} index 下标第几个
 */
const transVal = (index, yVal, n = (Ydistance / ySpan)) => {
    let x = Xdistance + 15 + (Xdistance) * index;
    let y = (285 - yVal * n);
    return {
        x, y
    }
};
// 求数组最大值
const maxVal = (arr) => {
    return Math.max(...arr);
};
/**
 * 画线
 * @param  {Array}    coordinates     对象数组坐标 [{x,y},{x,y}]
 * @param  {String}   lineColor       线条颜色
 * @param  {Number}   lineWidth       线条宽度
 * @param  {String}   lineCap         线帽 "round/butt/square" 圆形、默认、方形
 * @param  {String}   lineJoin        设置线段连接处为默认/圆形/平直形式 miter/round/bevel
 * @param  {Number}   globalAlpha     设置图案的透明度
 * 
 */
const drawLine = ({ coordinates, lineColor, lineWidth, lineCap, lineJoin, globalAlpha }) => {
    // 开启新路径
    ctx.beginPath();
    coordinates.forEach((coordinate, i) => {
        if (i == 0) {
            ctx.moveTo(coordinate.x, coordinate.y);
        } else {
            ctx.lineTo(coordinate.x, coordinate.y);
        }
    })
    ctx.strokeStyle = lineColor;
    ctx.lineWidth = lineWidth;
    ctx.lineCap = lineCap;
    ctx.lineJoin = lineJoin;
    ctx.globalAlpha = globalAlpha;
    // 画线
    ctx.stroke();
    // 关闭新路径
    ctx.closePath();
};

/**
 * 画矩阵
 * @param  {Array}    coordinates     [x,y,width,height]
 * @param  {String}   lineColor       空心矩阵线条颜色
 * @param  {String}   fillColor       实体矩阵线填充颜色
 * @param  {Number}   lineWidth       空心矩阵线条宽度
 * @param  {Number}   globalAlpha     设置图案的透明度
 * 
 */
const drawRect = ({ coordinates, lineColor, lineWidth, globalAlpha, type = 'empty', fillColor }) => {
    // 开启新路径
    ctx.beginPath();

    // 空心矩阵
    if (type == 'empty') {
        ctx.strokeStyle = lineColor;
        ctx.lineWidth = lineWidth;
        ctx.globalAlpha = globalAlpha;
        ctx.strokeRect(...coordinates);
        // 或者
        // ctx.rect(...coordinates)
        // ctx.stroke();
    }
    if (type == 'fill') {
        ctx.fillStyle = fillColor;// 填充颜色一定要写在生成矩形（fillRect()）之前，否则颜色不生效
        ctx.globalAlpha = globalAlpha;
        ctx.fillRect(...coordinates);
        // 或者
        // ctx.rect(...coordinates)
        // ctx.fill();
    }

    // 开启新路径
    ctx.closePath();
};

/**
 * 绘制文字
 *  @param  {Number}    x              x
 *  @param  {Number}    y              y
 *  @param  {String}    fontSize       字体大小px
 *  @param  {Number}    maxWidth       最大宽度px
 */
const drawFont = ({ x, y, text, fontSize, maxWidth, fillColor }) => {
    ctx.beginPath();
    ctx.font = fontSize;
    ctx.fillStyle = fillColor;
    ctx.fillText(text, x, y, maxWidth);
    ctx.closePath();
};

/**
 * 画弧线
 * @param  {Number}    x              圆心 x
 * @param  {Number}    y              圆心 y
 * @param  {Number}    radius         半径长度
 * @param  {Number}    startAngle     开始角度
 * @param  {Number}    endAngle       结束角度
 * @param  {Boolean}   anticlockwise  false顺时针 true逆时针 
 * @param  {String}    lineColor      线条颜色
 * @param  {Number}    lineWidth      线条宽度
 * @param  {Number}    globalAlpha    透明度
 * 
 */
const drawArc = ({ x, y, radius, startAngle, endAngle, anticlockwise, lineColor, lineWidth, globalAlpha }) => {
    ctx.beginPath();
    ctx.strokeStyle = lineColor;
    ctx.lineWidth = lineWidth;
    ctx.globalAlpha = globalAlpha;
    ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise) // 不写anticlockwise项的话默认为顺时针
    // ctx.lineTo(x, y);
    // ctx.closePath() ; //用于连接首尾点
    ctx.stroke();
    ctx.closePath();
};
</script>

<style lang="less" scoped>
h3 {
    text-align: center;
    position: fixed;
    font-size: 0.25rem;
    left: 50%;
    top: 22%;
    transform: translate(-50%, -50%);
}
.box {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);

    background: #e2e2e269;
}

.canvas {
    font-size: 0.25rem;

    .search {
        padding: 0.3rem;

        span {
            margin-left: 0.4rem;
        }
    }

    .tk {
        position: absolute;
        background: rgba(76, 226, 246, 0.708);
        transform: translate(-50%, -50%);
        padding: 0.05rem;
        // width: 2rem;
        font-weight: bold;
        color: rgb(78, 78, 78);
        height: 0.35rem;
        min-width: 1.5rem;
        text-align: center;
        border-radius: 0.1rem;
        font-size: 0.12rem;
        z-index: 1;
    }

    .dot {
        position: absolute;
        background: rgba(29, 0, 250, 0.708);
        transform: translate(-50%, -50%);
        width: 0.06rem;
        height: 0.06rem;
        border-radius: 50%;
        z-index: 2;
    }
}
</style>